package com.linked.gateway.router;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.linked.gateway.config.GatewayRouterConfig;
import com.linked.gateway.config.GatewayRouterInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

/**
 * @author :dbq
 * @date : 2022/9/15 8:51
 */
@Component
public class DynamicRouteConfiguration {
    @Autowired
    private GatewayRouterConfig gatewayRouterConfig;

    @Autowired
    private DynamicRouteEventPublisher publisher;

    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;

//    @Autowired
//    public DynamicRouteConfiguration(GatewayRouterConfig gatewayRouterConfig, DynamicRouteEventPublisher publisher, RouteDefinitionWriter routeDefinitionWriter) {
//        this.routeDefinitionWriter = routeDefinitionWriter;
//        this.gatewayRouterConfig = gatewayRouterConfig;
//        this.publisher = publisher;
//    }

    private final static Logger LOGGER = LoggerFactory.getLogger(DynamicRouteConfiguration.class);

    @Bean
    public void refreshGatewayRouter() throws NacosException {
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, gatewayRouterConfig.getServiceAddr());
        properties.put(PropertyKeyConst.NAMESPACE, gatewayRouterConfig.getNamespace());

        ConfigService configService = NacosFactory.createConfigService(properties);


        /**
         * 一、初始的路由数据要发布一下
         * */
        initRouterpublisher(configService);

        /**
         * 二、创建nacos监听
         * */
        Listener listener = new Listener() {
            @Override
            public Executor getExecutor() {
                return null;
            }

            @Override
            public void receiveConfigInfo(String configInfo) {
                LOGGER.info(configInfo);
                //把配置文件读取为RouteDefinition实体类列表
                List<RouteDefinition> routeDefinitionList = transformRouteDefinitions(configInfo);
                //把多个路由发布
                routeReplacements(routeDefinitionList);
            }
        };
        configService.addListener(gatewayRouterConfig.getDataId(), gatewayRouterConfig.getGroup(), listener);

    }

    /**
     * 初始化-发布路由
     */
    public void initRouterpublisher(ConfigService configService) throws NacosException {

        LOGGER.info("初始化路由：");

        String localConfig = configService.getConfig(gatewayRouterConfig.getDataId(), gatewayRouterConfig.getGroup(), 30000);

        LOGGER.info("\n" + localConfig);

        List<RouteDefinition> routeDefinitionList = transformRouteDefinitions(localConfig);

        routeReplacements(routeDefinitionList);
    }

    /**
     * 配置文件String转化RouteDefinition类列表
     */
    public List<RouteDefinition> transformRouteDefinitions(String config) {
        List<GatewayRouterInfo> gatewayRouters = null;
        List<RouteDefinition> routeDefinitionList = null;
        try {
            gatewayRouters = JSONObject.parseArray(config, GatewayRouterInfo.class);

        } catch (Exception e) {
            LOGGER.info("JSON格式转化错误！");
        }
        if (!CollectionUtils.isEmpty(gatewayRouters)) {
            routeDefinitionList = new ArrayList<>();
            for (GatewayRouterInfo temp : gatewayRouters) {
                RouteDefinition routeDefinitionTemp = transformRouteDefinition(temp);
                routeDefinitionList.add(routeDefinitionTemp);
            }
        }
        return routeDefinitionList;
    }

    /**
     * 单个实体类转化RouteDefinition类
     */
    public RouteDefinition transformRouteDefinition(GatewayRouterInfo routerInfo) {

        RouteDefinition routeDefinition = new RouteDefinition();
        routeDefinition.setId(routerInfo.getId());
        routeDefinition.setOrder(routerInfo.getOrder());
        routeDefinition.setUri(UriComponentsBuilder.fromUriString(routerInfo.getUri()).build().toUri());
        routeDefinition.setFilters(routerInfo.getFilters().stream().map(tempFilter -> {
            FilterDefinition filterDefinition = new FilterDefinition();
            filterDefinition.setArgs(tempFilter.getArgs());
            filterDefinition.setName(tempFilter.getName());
            return filterDefinition;
        }).collect(Collectors.toList()));

        List<PredicateDefinition> predicates = routerInfo.getPredicates().stream().map(tempPred -> {
            PredicateDefinition definition = new PredicateDefinition();
            definition.setArgs(tempPred.getArgs());
            definition.setName(tempPred.getName());
            return definition;
        }).collect(Collectors.toList());
        routeDefinition.setPredicates(predicates);
        return routeDefinition;
    }

    /**
     * 多个路由发布
     */
    public void routeReplacements(List<RouteDefinition> routeDefinitions) {
        routeDefinitions.stream().forEach(temp -> routeReplacement(temp));
    }

    /**
     * 单个路由发布
     */
    public void routeReplacement(RouteDefinition routeDefinition) {
        try {
            routeDefinitionWriter.delete(Mono.just(routeDefinition.getId()));
            LOGGER.info(routeDefinition.getUri().toString() + "路由删除成功");
        } catch (Exception e) {
            LOGGER.error(routeDefinition.getUri().toString() + "路由删除异常:{}", e.getMessage());
        }
        try {
            routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
            LOGGER.info(routeDefinition.getUri().toString() + "路由更新成功");
        } catch (Exception e) {
            LOGGER.error(routeDefinition.getUri().toString() + "路由更新异常:{}", e.getMessage());
        }
    }
}
